﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Collections;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace Server
{
    /// <summary>
    /// Questa classe racchiude le informazioni relative al cliente , e fornisce i metodi per comunicare con il cliente associato
    /// all'innterno della classe è presente un socket che deve essere già connesso. altrimenti si lancia un eccezione
    /// </summary>
    class Cliente
    {
        #region Variabili

        private string nome;

        #region Socket
        private Socket chat_socket;
        private Socket frame_socket;
        private Socket clipboard_socket;
        private bool frame_socket_set = false;
        #endregion

        private Object lock_chat;
        private Object lock_frame;
        private Object lock_clipboard;

        private bool autenticatoflag = false;

        #endregion

        #region Costruttori
        /// <summary>
        /// Costruttore della classe Cliente
        /// </summary>
        /// <param name="nome">Il nome del cliente</param>
        /// <param name="s"> Un socket connesso altrimenti generazione di una eccezione</param>
        public Cliente(string nome, Socket s)
        {
              
               this.nome = nome;
               if (s.Connected)
                   this.chat_socket = s;
               else
               {
                   throw new Exception("Socket non connesso", null);
               }
               lock_chat = new Object();
               lock_frame = new Object();
               lock_clipboard = new Object();
        }
        private Cliente(){}
        #endregion


        /// <summary>
        /// Questa funzione invia all'istanza del cliente il messaggio selezionato (prima divide il messaggio in vari pacchetti e li invia)
        /// </summary>
        /// <param name="m">Il messaggio da inviare al cliente</param>
        public void invia(Messaggio m)
        {
            try
            {
                if (m.isFrame)
                {
                    lock (lock_frame)
                    {
                        NetworkStream stream = new NetworkStream(this.frame_socket);
                        BinaryFormatter formatter = new BinaryFormatter();
                        formatter.Serialize(stream, m);
                        stream.Close();
                    }
                }
                else if (m.isClipboardObject)
                {
                    lock (lock_clipboard)
                    {
                        NetworkStream stream = new NetworkStream(this.clipboard_socket);
                        BinaryFormatter formatter = new BinaryFormatter();
                        formatter.Serialize(stream, m);
                        stream.Close();
                    }
                }
                else
                {
                    lock (lock_chat)
                    {
                        NetworkStream stream = new NetworkStream(this.chat_socket);
                        BinaryFormatter formatter = new BinaryFormatter();
                        formatter.Serialize(stream, m);
                        stream.Close();
                    }
                }
            }
            catch (Exception)
            {
                // devo gestire gli errori
                // in questo caso divrebbe essere sufficiente catchare poiché se non riesce ad inviare vuol dire che il client si è disconnesso a quel punto
                // sarà un'altra funzione ad accorgesene
            }
        }
       
        #region Ricezione Messaggio
        public delegate Messaggio RiceviMsgDelegate(); //delegato per poter chiamare RiceviMsg in maniera Asincrona

        /// <summary>
        /// Questa funzione si occupa di chiamare la funzione di callback passatagli quando arriva un nuovo messaggio, tuttavia non è bloccante per il programma
        /// N.B 
        /// Per adesso l'ho implementata facendo si che la massima dimensione che possa arrivare sia 32767*4 byte dato che il testo è un massimo di 32767 caratteri unicode
        /// 
        /// </summary>
        public void ricevi(AsyncCallback callback)
        {
            
            RiceviMsgDelegate d = new RiceviMsgDelegate(RiceviMsg);
            IAsyncResult  result = d.BeginInvoke(callback, this.chat_socket);
           
        }

        /// <summary>
        /// Funzione di uility che viene eseguita su un thread separato e si pone in attesa di messaggi 
        /// Quando il messaggio è arrivato allora si sblocca e quando chaimo endInvoke nella callback ritorna il risultato
        /// </summary>
        /// <returns></returns>
        private Messaggio RiceviMsg()
        {
            try
            {
                BinaryFormatter formatter = new BinaryFormatter();
                NetworkStream stream = new NetworkStream(this.chat_socket);
                Messaggio m = (Messaggio)formatter.Deserialize(stream);
                return m;
            }
            catch (Exception)
            {
                return null;
            }
            
        }
        #endregion

        #region Proprietà
        public string NomeCliente {
            get { return this.nome; }
            set {this.nome = value;}
        }
        public Socket Socket
        {
            get { return this.chat_socket; }
        }
        public bool autenticato { get { return this.autenticatoflag; } set { this.autenticatoflag = value; } }

        public Socket FrameSocket
        {
            get { return this.frame_socket; }
            set {
                this.frame_socket_set = true; 
                this.frame_socket = value; }
        }
        public Socket ClipboardSocket
        {
            get { return this.clipboard_socket; }
            set
            {
                this.clipboard_socket = value; 
            }
        }

        public bool FrameSocketSet { get { return this.frame_socket_set; } }
        #endregion





    }
   
}
